<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 
    1. login页面
      1.1 绘制登录页面的基本结构  表单的结构
      1.2 配置表单的校验规则  mobile password
      1.3 配置勾选用户协议规则  自定义规则 validator: (rule, value, cb)=> {  }
      1.4 登录前的预校验 validate 
      1.5 测试发送登录的请求  报跨域的问题
    2. 配置了request模块  axios 基地址 请求拦截器 响应拦截器  并导出
    3. 封装登录的接口  报跨域的问题
    4. 解决跨域的问题
        4.1 vue.config.js 配置文件 注释 mock代码  devServer: {proxy: {'/api': {target: '真正的服务器地址'}}}
        4.2 修改request 模块 baseURL 为 http://localhost:9528/api  测试代理服务是否成功
            重启项目
     5. 环境变量文件 .env文件   根据不同的开发环境, 自动加载对应的环境变量 yarn dev  .env.development这个文件会自动执行 在这个文件中配置了 VUE_APP_BASE_API = '/api' 将 axios的基地址设置为 process.env.VUE_APP_BASE_API
     6. store/modules/user.js 准备登录的action  和 mutation state 存储 token 
     7. 配合 utils/auth 实现token的持久化存储
     8. 登录成功的后续操作  登录提示和页面的跳转 1. action中将res返回 login页面根据success字段分支判断
     9. 响应拦截器中 第一个处理函数中 判断success字段 并提示错误 抛出异常的Promise
     10. 请求拦截器中统一挂载token
     11. 响应拦截器的第二个处理函数处理token过期  清除失败的token 跳转到登录页面
     10+11 配合 获取个人信息
   -->

   <!--
     1. permission.js 导航守卫 页面拦截工作  是否有token  
         有token 判断是否去的是登录页面  强制去首页  反之 直接放行
         没有token 判断是否去的是白名单  放行 反之强制登录
     2. 配置前置和后置导航守卫 做 进度条  
        1. 导入nprogress 和 nprogress样式文件
        2. 前置导航守卫中 start()  后置导航守卫  done()
        3. 被拦截 不会经过后置守卫  手动 done()一次
     3. 为登录按钮添加 loading  重复点击登录按钮 
     4. 在user模块 获取个人信息 定义action mutation state 存储个人信息  action触发先在页面的组件钩子created中触发的
      4.1 优化 将获取个人信息 触发action操作 放在 前置导航守卫 有token 访问非登录页面的分支中
      4.2 处理切换页面多次获取个人信息  if(没有用户名) { 触发action操作获取个人信息 }
    5. 将名称和头像处理为全局的getters渲染到navbar组件
    6. 文本头像 name ? name.slice(0,1) : '' 姓     
    7. 退出功能
        1. 清除token + 个人信息  单独做了logout的action
        2. 提示
        3. 跳转到登录页面
        4. 改造401 token过期  触发 logout的action
    8. .native修饰符 为组件绑定原生事件发现不成功的时候  
    9. 修改密码
       9.1 准备的对话框组件 dialog
       9.2 表单结构的绘制 + 表单关联的字段 + 表单校验规则
       9.3 准备修改密码的接口api 
       9.4 为确定按钮绑定点击事件
           1. 预校验
           2. 携带参数发送请求
           3. 提示
           4. 关闭对话框并重置表单
           5. 清除token和个人信息触发 logout的action
           5. 退出重新登录
      9.5 为对话框绑定了close事件   关闭对话框 并 重置表单 取消按钮绑定这个处理函数
      10. 路由规则梳理(可选)
    -->

   <!--
    1. 页面路由组件和规则
      1.1 views 新建了各个页面的路由组件 并配置了路由规则
      1.2. 在router/modules/多个页面的路由.js 模块   每个js模块放置的对应的路由规则
      1.3. 统一导入到了 router/index.js 挂载 路由规则 
      1.4. 拆分了 静态路由和动态路由 合并到 routes 
    2. 处理左侧的logo展示  layout/components/sidebar/logo.vue 
    3. 组织页面
       3.1 认知树形组件 el-tree :data="[{label:'', children: [{}]}]"
                 :props="{label:'xx', children: 'xxx'}"
                 展开所有节点 default-expand-all
                 禁止点击文本展开和收起 :expand-on-click-node="false"
       3.2 树形组件使用插槽配置左右结构  el-row el-col  el-drop-down  
       3.3 封装获取部门列表的接口api 在组件中调用
       3.4 对部门id和pid依赖关系梳理,定义了递归处理函数 将数据处理成树形数据
       3.5 树形数据处理的函数 抽离 utils/index.js中
       3.6 为三个按钮使用command绑定了事件
       3.7 准备新增部门的对话 定义好布尔值
       3.8 抽离新增部门对话框的结构为子组件 布尔值放在父组件中 使用.sync传递给子组件 子组件update:xxx 更新同步布尔值
       3.9 准备表单结构 + 准备表单数据 + 基本的校验规则
       3.10 部门名称和部门编码不允许重复的校验规则
           1. 在父组件存储了原始的部门列表
           2. 传递给子组件原始的部门列表
           3. 子组件定义 自定义校验规则 判断

     -->

   <!--
     1. 新增部门
       1.1 部门负责人列表的渲染
          1.1.1 准备获取部门负责人的接口api
          1.1.2 调用接口 使用 select组件渲染  label 展示的字段  value是收集提交的字段
       1.2 新增部门pid字段
           两种记录方式 1. 为按钮绑定@click.native    记录id  并传递给addDept组件
                       2. 为command事件 传参 @command="handleClick($event, data.id)" 记录id  并传递给addDept组件
       1.3 准备新增的接口API
           预校验
           发送新增的请求
           提示
           关闭对话框并重置表单
           通知父组件重新获取部门列表                
    2. 编辑部门
      2.1 点击编辑部门按钮 展示对话框
      2.2 在addDept子组件内定义根据id获取部门信息的方法  父组件通过$refs的方式访问到子组件的方法
      2.3 传递id 方式一有坑
      2.4 准备获取部门详情的接口API 调用并将返回的数据交给 form 回显
      2.5 处理编辑状态下的部门名称和部门编码的问题
         2.5.1 备份部门详情信息
         2.5.2 调整自定义校验规则 如果处于编辑状态并且输入框的值是和当前编辑部门/编码的名称一样,校验通过
      2.6 准备编辑提交的接口API, 拆分了确认按钮的处理函数的业务   有id走编辑提交的请求 反之走新增的请求
      2.7 先编辑再新增 bug  close处理函数中 手动将form 重置(form重置只会重置掉form表单关联的字段)
    3. 删除部门
       3.1 记录id  
       3.2 准备删除部门的接口API
       3.3 删除的处理函数中
           确认消息提示
           发送删除的请求
           提示
           重新获取部门列表      

     4.角色搭建页面的基本结构
     5. 准备了获取角色列表的接口API 并调用 渲染到表格中
       5.1 状态 作用域插槽处理
       5.2 操作列 作用域插槽处理
     6. 分页组件 属性  事件绑定ok
     7. 新增角色
       7.1 准备好新增的对话框
       7.2 准备表单结构和数据字段和校验规则  处理switch开关组件  激活的值和失活的值
       7.3 新增角色 
           准备新增角色的接口API
           预校验
           发送新增的请求
           提示
           关闭对话框并重置表单
          重新获取角色列表
     -->
    
   <!-- 
    1.编辑角色
      1.1 在数据赋值之前遍历数据给数据新增的isEdit字段布尔值
      1.2 点击编辑按钮的时候 将当前行的isEdit设置为true
      1.3 每一列中都处理成插槽的形式 基于isEdit 互斥渲染两种结构
      1.4 数据回显
         在数据赋值之前遍历数据给数据新增的editRow对象记录当前行的角色信息 将这个对象双向绑定给表单和开关
         在编辑状态下点击取消后数据还原
      1.5 编辑提交
         1.5.1 准备编辑提交的接口API
         1.5.2 点击确认后 发送请求携带参数  editRow + id字段 
         1.5.3 提示
         1.5.4 直接反向将editRow的数据同步给每一行的row对应的字段 (减少请求)
         1.5.5 Object.assign合并属性
    2. 角色删除
       2.1 准备气泡弹窗 onConfirm事件 onCancel
       2.2 准备删除的接口API 
       2.3 发送请求
       2.4 提示
       2.5 如果只剩一条数据 让page-- 重新获取一次

    3. 员工页面 搭建了左树右表的结构
    4. 左树
       发送请求获取部门数据并处理成树形数据交给tree组件渲染   
          ref="treeRef"
          :data="treeData"  数据源
          :props="{ label: 'name' }" 配置
          default-expand-all 默认展开所有
          :expand-on-click-node="false" 禁止点击文本收起折叠
          highlight-current 高亮
          node-key="id" 唯一标识
          @node-click="handleClick" 点击树形节点的处理函数
    5. 右表    
      5.1 准备的表格的结构
      5.2 准备接口api 发送请求 存储员工列表数据 在表格中渲染
      5.3 单独处理头像列和聘用形式列  插槽
      5.4 分页
    6. 联动
        1. 数据字段 page pagesize departmentId  keyword   
        记录departmentId 两个地方记录  1. 获取到组织数据 默认赋值 treeData[0].id 
                                      2. 点击树形节点handleClick 记录
        2.keyword 
         1. 双向绑定
         2. @input事件
         3. 处理函数中
            1. 判断为空
            2. 让pege = 1
            3. 防抖发送请求
         4. clearable属性  @clear事件
         
         作业 滚动条
    -->
    
  <!--  
    1. 员工 左树的滚动条 left盒子 el-scrollbar 渗透样式
    2. 员工导出
        1. 准备了导出员工的接口API 单独指定响应的数据类型格式 responseType: 'blob'
        2. 单独对响应拦击器处理 对blob判断单独处理
        3. 安装file-saver  使用.saveAs(blob数据, '文件.扩展名')方法
    3. 员工导入
      3.1 准备导入子组件 并且父组件 .sync传递布尔值 点击导入按钮 将布尔值设置为true
      3.2 下载导入模板  
          准备了导入员工模板的接口API 单独指定响应的数据类型格式 responseType: 'blob'   
          使用.saveAs(blob数据, '文件.扩展名')方法
      ===== 梳理上传文件的业务  
             上传功梳理
          结构: 1. 必然是存在<input type="file">  
                2.accept 属性 可以限制上传文件的类型 
                3. 一般文件域是会隐藏的
                4. 准备自定义的选择文件的按钮
          业务: 1. 点击自定义的按钮实现文件的选择     点击自定义按钮触发文件域的click事件 
                2. 选择文件后 一般获取到上传文件对象  监听文件域的change事件, 在change事件的处理函数中获取到文件对象
                3. 获取到文件对象后, 发送ajax 上传的请求   
                4. 需要将文件域的value值清空
        3.3 上传员工excel
          3.3.1  点击自定义按钮触发文件域的click事件 
          3.3.2 为文件域绑定change事件,获取到上传的文件对象 e.target.files[0]
          3.3.3 准备上传的接口API
          3.3.4 将数据处理到FormData对象中
          3.3.5 发送上传的请求 
          3.3.6 提示 
          3.3.7 关闭对话框 通知父组件重新获取
       3.4 拖拽上传===================>>>>>          
          熟悉拖拽的事件  dragenter dragover  dragleave  drop    vuedrageable包 自行研究
          配合 阻止默认行为和阻止事件冒泡
          drop e.dataTransfer.files[0]
          3.3.3 准备上传的接口API
          3.3.4 将数据处理到FormData对象中
          3.3.5 发送上传的请求 
          3.3.6 提示 
          3.3.7 关闭对话框 通知父组件重新获取
    4. 新增员工 
        1. 准备路由规则和组件
        2. cv页面的结构+数据+校验规则
        3. 自行关联结构 数据 规则
        4. 聘用形式  单独准备 option下拉菜单
        5. 部门选择  级联选择器 options数据源 props配置
            5.1 获取部门列表并处理成树形数据 在utils/index transfromToTreeData  判断 if (children.length) item.children = children
            5.2 v-model收集到的是 选中的value值 ,存储到一个数组中
        6. 新增
          1. 预校验
          2. 准备接口并调用  单独处理departmentId 
          3. 发送请求
          4. 提示
          5. 返回上一个页面    
   -->
 
  <!-- 
   聊一下你对vue的了解?
    是什么? vue是一个前端渐进式的框架
    能干什么? 可以做单页面应用程序
    有什么好处? 双向绑定, 所有的功能都在一个页面上, 局部刷新, 用户体验好
    有什么弊端? 不利于seo 当项目过大首屏加载会很慢(白屏现象)
    这种问题如何解决?  路由懒加载  图片懒加载 组件数据懒加载  压缩 减少ajax请求  精灵图....
    应用场景? 
    结合自己的实际需求? 比如我最近做的一个项目
    相比于传统开发 jq  react 区别


    钩子函数的理解
      组件从创建到销毁, 经历了一个生命周期过程的, 主要分为几个阶段, 阶段中有一个钩子函数
      介绍每个阶段的钩子
      能干什么?  应用场景?  cretead  mounted  destory  keep-alive

    组件通信

     为什么?  组件的数据是独立的 不能直接相互访问的
     是什么? 
     方式: 

     what why how when 实际场景


     深浅拷贝


     promise 

     
     new Promise()



   -->
  <!-- 
   1. 查看员工
       1. 路由规则和新增员工的规则 公用同一个  参数可选配置 path: '/employee/detail/:id?', // ?可选
       2. 拿着route.params.id 获取员工信息 赋值给userInfo对象
       3. 禁用手机号输入框 :disabled="!!userInfo.id"
       4. 保存更新(编辑提交)
         4.1  更新的接口
         4.2. 拆分保存更新按钮的业务逻辑  如果有id走编辑提交, 没有id就走新增
         4.3  处理部门id
            如果没有更新部门信息 其实是可以携带userInfo直接保存的
            但是如果修改了部门信息, 就会触发侦听器, 取到最后部门的id 赋值给了 lastDeptId, 所以提交的时候就得携带lastDeptId
            解决方案, 获取到用户信息后立马存储lastDeptId一份  侦听器立即触发了 数据类型判断
            change事件更简单
   2. 创建image-upload子组件 并挂载
   3. :before-upload 文件大小和文件类型校验  设置action属性为""
   4. 腾讯云cos => 作用 做图片代管 
      4.1 注册并认证
      4.2 创建存储桶(公有读私有写) 并且开启跨域 创建一组秘钥 key Id
      4.3 安装cos-js-sdk-v5  并创建cos实例
      4.4 :http-request="处理函数" 覆盖了默认上传行为
          处理函数 使用腾讯云api向存储桶中发送请求存储数据
      4.5 在上传第二个回调函数中 $emit('input',腾讯云地址)     展示图片
      4.6 进度条 progress组件  和 onProgess处理函数progressData.percent 环形进度条
      4.7 本地实时预览 URL.createObjectURL()  FileReader()
    5. 权限管理页面结构 + 数据的获取 + 一级二级权限的渲染  
    作业:  权限的新增 + 权限的编辑 + 权限的删除
   -->
  
  <!-- 
   上一天作业:  权限的新增  权限的删除  权限的编辑
   权限: 先给员工分配角色, 再给角色分配权限   员工有了权限
   1.给员工分配角色 
      1.1点击角色按钮 展示一个对话框
      1.2使用复选框组 组件
      1.3封装获取已启用角色的接口 并获取到所有的角色列表  遍历展示复选框
      1.4获取员工详情 .roleIds 当前员工已有角色 赋值给 chekcbox复选框组关联的数组
      1.5封装为员工分配角色的接口  发送请求 携带员工id 和 勾选的复选框关联的数组 roleIds 发送请求
      1.6提示 
      1.7关闭对话框
      同时处理多个并发请求  
         等待机制
        Promise.all  同时处理多个并发请求  所有的结果返回 收集到一个数组中   只要有一个请求失败  整个就失败    
        Promise.allSettled 同时处理多个并发请求  所有的结果返回 收集到一个数组中    
        Promise.race 同时处理多个并发请求 只会拿到最快的一个响应结果  赛跑机制
        Promise.race([p1, p2, p3]).then(res=> { 谁响应最快 结果就是谁 })
   2. 给角色分配权限
     2.1 点击分配权限按钮 展示一个对话框     
     2.2 获取所有权限列表 并处理成树形数据 对话框中展示tree组件 show-checkbox展示复选框  check-strictly 父子互不关联
     2.3 封装获取角色详情的接口 角色详情中permIds 回显勾选 树形组件this.$refs.treeRef.setCheckedKeys(permIds)
         优化 :default-checked-key="permIds"
     2.4 确定按钮 封装为角色分配权限的接口 携带角色id和  permIds 
     2.5 提示
     2.6 关闭对话框

    3.  权限处理
     3.1. 拆分了 静态路由规则和动态路由规则 routes: constantRoutes
     3.2 permission.js中 导航守卫中 取到用户信息  前提在user/getUserInfo 中返回了个人信息
     3.2 个人信息中 roles.menus 当前用户的权限列表
     3.3 基于权限列表 筛选动态路由filter  再添加路由规则 addRoutes
     3.4 刷新 白屏 404  先将routes * 路由规则去除 在addRoutes完毕后 再next(to.path) 一次
     3.5 使用vuex维护管理 路由规则数据
       1. 新建store/moudles/routes.js 模块  维护路由规则数据
       2. layout/sidebar/index.vue  访问仓库的路由规则数据
       3. 准备setRoutes的mutation 在permission.js 获取到筛选后的路由规则后 触发这个mutation
       4. 退出重置路由规则  resetRoutes  nabvar.vue组件中触发

       操作权
    -->

    <!-- 

      按钮操作权:
        1. 访问权限下 新增了 按钮操作权限  唯一
        2. 处理按钮操作权 核心函数 返回一个布尔值  控制启用禁用/显示隐藏
        3. 函数的核心 user.userInfo.roles.points是否包含某些按钮权限点
        4. 全局的指令
        5. mixin 混入 不太好
       首页: 
          布局
          数据的请求和获取
          echarts  => 有宽高的容器
        打包: 
         三方包排除  cdn加速
         打包报告对比

         nginx 部署   xshell... 了解

         v-model  Object.defineProperty()
     -->
  </body>
</html>